﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Security.Cryptography.X509Certificates;
using System.Runtime.InteropServices;

namespace SMTPDialog
{
    public partial class SmtpForm : Form
    {
        // I'll be using the CryptUIDlgViewCertificate API and CRYPTUI_VIEWCERTIFICATE_STRUCT struct
        // available at CryptUI.dll, in order to show certificate details
        public const int CRYPTUI_DISABLE_ADDTOSTORE = 0x00000010;

        public struct CRYPTUI_VIEWCERTIFICATE_STRUCT
        {
            public int dwSize;
            public IntPtr hwndParent;
            public int dwFlags;
            [MarshalAs(UnmanagedType.LPWStr)]
            public String szTitle;
            public IntPtr pCertContext;
            public IntPtr rgszPurposes;
            public int cPurposes;
            public IntPtr pCryptProviderData; // or hWVTStateData
            public Boolean fpCryptProviderDataTrustedUsage;
            public int idxSigner;
            public int idxCert;
            public Boolean fCounterSigner;
            public int idxCounterSigner;
            public int cStores;
            public IntPtr rghStores;
            public int cPropSheetPages;
            public IntPtr rgPropSheetPages;
            public int nStartPage;
        }

        [DllImport("CryptUI.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public extern static Boolean CryptUIDlgViewCertificate(
            ref CRYPTUI_VIEWCERTIFICATE_STRUCT pCertViewInfo,
            ref bool pfPropertiesChanged
        );

        X509Certificate serverCert;
        string authDomain = "", authUser = "", authPass = "";

        public SmtpForm()
        {
            InitializeComponent();
        }

        private void sendButton_Click(object sender, EventArgs e)
        {
            if (serverText.Text.Length == 0)
            {
                MessageBox.Show("You must enter a SMTP server.", "Warning");
                return;
            }
            SMTPMailer mailer = new SMTPMailer(serverText.Text, Convert.ToInt32(portText.Text), 
                tlsCheck.Checked, ehloRadio.Checked);
            if (mailer.Auth = authCheck.Checked)    // Sí, es una asignación que devuelve un bool !!
            {
                if (authCombo.SelectedItem == null)
                {
                    MessageBox.Show("You must choose an authentication method.", "Warning");
                    authCombo.Focus();
                    SendKeys.Send("{F4}");
                    return;
                }
                mailer.AuthMethod = authCombo.SelectedItem.ToString();
                mailer.AuthDomain = authDomain;
                mailer.AuthUser = authUser;
                mailer.AuthPass = authPass;
            }
            mailer.SmtpDomain = domainText.Text;
            mailer.MailFrom = mailFromText.Text;
            mailer.RcptTo = rcptToText.Text;
            mailer.Data = dataText.Text;
            mailer.Worker = mailerWorker;

            sendButton.Enabled = false;
            tabControl.SelectTab("smtpPage");
            stopButton.Enabled = true;
            mailerWorker.RunWorkerAsync(mailer);
        }

        private void mailFromText_Leave(object sender, EventArgs e)
        {
            if (!dataText.Text.Contains("From:"))
                dataText.AppendText("From: " + mailFromText.Text + Environment.NewLine);
        }

        private void rcptToText_Leave(object sender, EventArgs e)
        {
            if (!dataText.Text.Contains("To:"))
                dataText.AppendText("To: " + rcptToText.Text + Environment.NewLine);
        }

        /* EXCHANGE 2007 AUTH OPTIONS
         * Authentication options in the receive connectors correspond to the 
         * following responses to the EHLO command.
         *   Transport Layer Security (TLS)
         *     STARTTLS [implemented]
         *   Basic authentication
         *     AUTH LOGIN
         *   Windows integrated authentication
         *     AUTH NTLM
         *   Exchange Server authentication
         *     X-ANONYMOUSTLS
         *     AUTH
         *     X-EXPS GSSAPI NTLM
         *     XEXCH50
         *     XRDST
         *   Externally protected authentication
         *     XEXCH50
         */
        private void authCheck_CheckedChanged(object sender, EventArgs e)
        {          
            if (authCheck.Checked)
            {
                authCombo.Enabled = true;
                authQueryButton.Enabled = true;
                authConfigButton.Enabled = true;
                ehloRadio.Checked = true;
                heloRadio.Enabled = false;
            }
            else
            {
                authCombo.Enabled = false;
                authQueryButton.Enabled = false;
                authConfigButton.Enabled = false;
                if (!tlsCheck.Checked)
                {
                    heloRadio.Enabled = true;
                    heloRadio.Checked = true;
                }
            }
        }

        private void mailerWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            SMTPMailer mailer = (SMTPMailer)e.Argument;
            // If SendMail() throws an exception, mailerWorker will report it as an Error
            mailer.SendMail();
        }

        private void mailerWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            switch (e.ProgressPercentage)
            {
                case 10:
                    serverCert = (X509Certificate)e.UserState;
                    certButton.Enabled = true;
                    break;
                default:
                    dialogText.AppendText((string)e.UserState);
                    break;
            }
        }

        private void mailerWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
                MessageBox.Show(e.Error.Message, "Error");
            sendButton.Enabled = true;
            stopButton.Enabled = false;
            dialogText.AppendText(Environment.NewLine);
        }

        private void stopButton_Click(object sender, EventArgs e)
        {
            mailerWorker.CancelAsync();
        }

        private void clearButtom_Click(object sender, EventArgs e)
        {
            dialogText.Clear();
        }

        private void tlsCheck_CheckedChanged(object sender, EventArgs e)
        {
            if (tlsCheck.Checked)
            {
                ehloRadio.Checked = true;
                heloRadio.Enabled = false;
            }
            else if (!authCheck.Checked)
            {
                heloRadio.Enabled = true;
                heloRadio.Checked = true;
            }
        }

        private void certButton_Click(object sender, EventArgs e)
        {
            // Show the certificate dialog
            CRYPTUI_VIEWCERTIFICATE_STRUCT certViewInfo = new CRYPTUI_VIEWCERTIFICATE_STRUCT();
            certViewInfo.dwSize = Marshal.SizeOf(certViewInfo);
            certViewInfo.pCertContext = serverCert.Handle;
            certViewInfo.szTitle = "Certificate Info";
            certViewInfo.dwFlags = CRYPTUI_DISABLE_ADDTOSTORE;
            certViewInfo.nStartPage = 0;
            bool fPropertiesChanged = false;
            if (!CryptUIDlgViewCertificate(ref certViewInfo, ref fPropertiesChanged))
            {
                int error = Marshal.GetLastWin32Error();
                MessageBox.Show(error.ToString());
            }

        }

        private void authQueryButton_Click(object sender, EventArgs e)
        {
            if (serverText.Text.Length == 0)
            {
                MessageBox.Show("You must enter a SMTP server.", "Warning");
                return;
            }
            SMTPMailer mailer = new SMTPMailer(serverText.Text, Convert.ToInt32(portText.Text),
                tlsCheck.Checked, ehloRadio.Checked);
            mailer.SmtpDomain = domainText.Text;
            mailer.Worker = authQueryWorker;
            authQueryButton.Text = "Please, wait";

            authCombo.Enabled = false;
            authQueryButton.Enabled = false;
            authConfigButton.Enabled = false;
            authQueryWorker.RunWorkerAsync(mailer);
        }

        private void authQueryWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            SMTPMailer mailer = (SMTPMailer)e.Argument;
            e.Result = mailer.GetAuthMethods();
        }

        private void authQueryWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            bool success = false;
            if (e.Error != null)
            {
                MessageBox.Show(e.Error.Message, "Error");
            }
            else
            {
                string[] authMethods = (string[])e.Result;
                dialogText.AppendText(Environment.NewLine);
                if (authMethods == null)
                {
                    MessageBox.Show("Error while querying available authentication methods", "Error");
                }
                else
                {
                    int count = 0;
                    foreach (string method in authMethods)
                        ++count;

                    if (count == 0 || authMethods[0].Length == 0)
                    {
                        authQueryButton.Text = "Query";
                        authCheck.Checked = false;
                        MessageBox.Show("The server doesn't offer any authentication method", "Warning");
                        return; // No quiero habilitar los botones al final
                    }
                    else
                    {
                        authCombo.Items.Clear();
                        authCombo.Items.AddRange(authMethods);
                        success = true;
                    }
                }
            }
            authCombo.Enabled = true;
            authQueryButton.Text = "Query";
            authQueryButton.Enabled = true;
            authConfigButton.Enabled = true;
            if (success)
            {
                authCombo.Focus();
                SendKeys.Send("{F4}");
            }
        }

        private void authQueryWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            switch (e.ProgressPercentage)
            {
                case 10:
                    serverCert = (X509Certificate)e.UserState;
                    certButton.Enabled = true;
                    break;
                default:
                    dialogText.AppendText((string)e.UserState);
                    break;
            }
        }

        private void authConfigButton_Click(object sender, EventArgs e)
        {
            string method;
            if (authCombo.SelectedItem == null)
            {
                MessageBox.Show("You must first choose an authentication method.", "Warning");
                authCombo.Focus();
                SendKeys.Send("{F4}");
            }
            else if ((method = authCombo.SelectedItem.ToString()) == "LOGIN" ||
                method == "NTLM")
            {
                AuthOptions options = new AuthOptions();
                options.Domain = authDomain;
                options.Username = authUser;
                options.Password = authPass;
                if (options.ShowDialog(this) == DialogResult.OK)
                {
                    authDomain = options.Domain;
                    authUser = options.Username;
                    authPass = options.Password;
                }
            }
            else
            {
                MessageBox.Show("The application doesn't implement this authentication method yet.", "Warning");
            }
        }

        private void dataText_Enter(object sender, EventArgs e)
        {
            if (!dataText.Text.Contains("From:"))
                dataText.AppendText("From: " + Environment.NewLine);
            if (!dataText.Text.Contains("To:"))
                dataText.AppendText("To: " + Environment.NewLine);
            if (!dataText.Text.Contains("Subject:"))
                dataText.AppendText("Subject: <Enter subject>" + Environment.NewLine + 
                    Environment.NewLine + "<Enter message body>" + 
                    Environment.NewLine + "." +
                    Environment.NewLine + Environment.NewLine);
        }




    }
}
